package com.ctw.tinyservices.id.portal.handler;

import com.ctw.tinyservices.id.common.dto.ResponseDTO;
import com.ctw.tinyservices.id.common.dto.helper.ResponseDTOHelper;
import com.ctw.tinyservices.id.common.enums.code.ErrorCodeEnum;
import com.ctw.tinyservices.id.common.utils.LogUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;

/**
 * @author TongWei.Chen  2022/4/18 18:15
 *
 * 统一异常处理器
 **/
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(DuplicateKeyException.class)
    @ResponseBody
    public ResponseDTO<String> exception(DuplicateKeyException e){
        LogUtils.error(GlobalExceptionHandler.class, "DuplicateKeyException ", e);
        return ResponseDTOHelper.fail(ErrorCodeEnum.DUPLICATE_KEY_ERROR, e.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseDTO<String> exception(MethodArgumentNotValidException e){
        LogUtils.error(GlobalExceptionHandler.class, "MethodArgumentNotValidException ", e);
        return resolverJsonBindException(e);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ResponseDTO<String> exception(ConstraintViolationException e){
        LogUtils.error(GlobalExceptionHandler.class, "ConstraintViolationException ", e);
        return resolverConstraintViolationException(e);
    }

    @ExceptionHandler(BindException.class)
    @ResponseBody
    public ResponseDTO<String> exception(BindException e){
        LogUtils.error(GlobalExceptionHandler.class, "BindException ", e);
        return resolverBindException(e);
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseDTO<String> exception(Exception e){
        LogUtils.error(GlobalExceptionHandler.class, "exception ", e);
        return ResponseDTOHelper.fail(e.getMessage());
    }

    private ResponseDTO<String> resolverConstraintViolationException(Throwable e) {
        ConstraintViolationException exception = (ConstraintViolationException) e;
        StringBuilder errorMsg = new StringBuilder();
        Set<ConstraintViolation<?>> constraintViolations = exception.getConstraintViolations();
        int i = 0;
        for (ConstraintViolation constraintViolation : constraintViolations) {
            errorMsg.append(constraintViolation.getMessageTemplate());
            if (constraintViolations.size() > 1 && i != constraintViolations.size() - 1) {
                errorMsg.append("并且");
            }
            i ++;
        }
        return ResponseDTOHelper.fail(ErrorCodeEnum.PARAM_ERROR, errorMsg.toString());
    }

    /**
     * 处理@RequestBody 参数绑定异常
     */
    private ResponseDTO<String> resolverJsonBindException(Throwable e) {
        MethodArgumentNotValidException argumentNotValidException = (MethodArgumentNotValidException) e;
        List<FieldError> errorList = argumentNotValidException.getBindingResult().getFieldErrors();
        String errorMsg = appendErrorMsg(errorList);
        return ResponseDTOHelper.fail(ErrorCodeEnum.PARAM_ERROR, errorMsg);
    }

    /**
     * 处理参数绑定异常
     */
    private ResponseDTO<String> resolverBindException(Throwable e) {
        BindException be = (BindException) e;
        List<FieldError> errorList = be.getBindingResult().getFieldErrors();
        String errorMsg = appendErrorMsg(errorList);
        return ResponseDTOHelper.fail(ErrorCodeEnum.PARAM_ERROR, errorMsg);
    }

    private String appendErrorMsg( List<FieldError> errorList) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0, size = errorList.size(); i < size; i ++) {
            FieldError error = errorList.get(i);
            sb.append(error.getDefaultMessage());
            if (size > 1 && i != size - 1) {
                sb.append(", ");
            }
        }
        return sb.toString();
    }
}
